package com.miyake.dsp.osa.lab;

import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.miyake.dsp.ComplexArray;
import com.miyake.dsp.osa.lab.Connector.InputListener;
import com.miyake.dsp.osa.lab.Connector.OutputListener;
import com.miyake.dsp.osa.lab.ui.CalculatorUiPanel;

public class LabContainer {
	public void addAutoDisabled(String k) {
		autoDisabledList.add(k);
	}

	private Map<String, Connector> inConnector = new HashMap<String, Connector>();
	private Map<String, Connector> outConnector = new HashMap<String, Connector>();
	private Calculator calculator;
	private CalculatorUiPanel ui;
	private InputListener inputListener;
	private OutputListener outputListener;
	private boolean bypass;
	private List<String> autoDisabledList = new ArrayList<String>();
	private boolean cancel;
	private Map<String, String> aliveConnectors = new HashMap<String, String>();
	
	public boolean isBypass() {
		return bypass;
	}

	public void setBypass(boolean bypass) {
		this.bypass = bypass;
	}

	public Map<String, Connector> getInConnectors() {
		return inConnector;
	}

	public Map<String, Connector> getOutConnectors() {
		return outConnector;
	}
	
	public LabContainer(final Calculator processor) {
		this.calculator = processor;

		// Tells data is updated to next component
		inputListener = new InputListener() {
			@Override
			public synchronized void onDataUpdate(Connector connector) {
				for (String key : inConnector.keySet()) {
					if (inConnector.get(key).equals(connector)) {
						if (autoDisabledList.contains(key)) {
							cancel = true; // Do not execute calculation
						}
						if (!isAliveConnectorChanged(key, connector.getCode())) {
							return;
						}

						break;
					}
				}

				process();
			}
		};
		// Requests data to previous component
		outputListener = new OutputListener() {
			@Override
			public void onDataRequest() {
				process();
			}
		};
		for (String key : processor.getInputMap().keySet()) {
			inConnector.put(key, new Connector(null, null));
		}
		for (String key : processor.getOutputMap().keySet()) {
			Connector oConnector = new Connector(this, key);
			oConnector.addOutputListener(outputListener);
			outConnector.put(key, oConnector);
		}
	}
	


	public Set<String> getInConnectorKeySet() {
		return inConnector.keySet();
	}
	
	public Set<String> getOutConnectorKeySet() {
		return outConnector.keySet();
	}
	
	public Connector getInConnector(String key) {
		return inConnector.get(key);
	}
	
	public Connector getOutConnector(String key) {
		return outConnector.get(key);
	}
	
	public void process() {
		ComplexArray bypassData = null;
		for (String key : inConnector.keySet()) {
			Connector con = inConnector.get(key);
			if (con != null) {
				ComplexArray data = con.getData(!autoDisabledList.contains(key));
				bypassData = data;
				calculator.getInputMap().put(key, data);
				//System.out.println(this.getCalculatorName() + key+" updateData()");
				ui.updateData(key, con);
			}
		}
		
		if (cancel) {
			cancel = false;
			return;
		}
		
		
		if (!isBypass()) {
			calculator.process();
		}
		System.out.println("Process:"+calculator.getClass().getName());
				
		for (String key : this.outConnector.keySet()) {
			ComplexArray data = calculator.getOutputMap().get(key);
			Connector con = outConnector.get(key);
			if (isBypass()) {
				con.setData(bypassData);
			}
			else {
				con.setData(data);
			}
			
			if (!con.isNowRequesting()) {
				ui.updateData(key, con);
			}
		}	
		for (String key : this.outConnector.keySet()) {
			outConnector.get(key).fire();
		}
	}

	public void setInConnector(String key, Connector connector) {
		inConnector.put(key, connector);
		connector.addInputListener(inputListener);
		aliveConnectors.put(key, connector.getCode());
	}
	
	public void setOutConnector(String key, Connector connector) {
		outConnector.put(key, connector);
		connector.addOutputListener(outputListener);
	}

	public void removeInConnector(String key) {
		inConnector.get(key).removeInputListener(inputListener);
		inConnector.put(key, new Connector(null, null));
		aliveConnectors.remove(key);
	}
	
	public void removeOutConnector(String key) {
		outConnector.get(key).removeOutputListener(outputListener);
	}
	
	public Object getSetting() {
		return this.calculator.getSetting();
	}

	public void setUI(CalculatorUiPanel ui) {
		this.ui = ui;
	}

	public CalculatorUiPanel getUI() {
		return this.ui;
	}

	public String getCalculatorName() {
		return calculator.getClass().getSimpleName();
	}
	
	public String getCaption() {
		String caption = getCalculatorName();//getCalculator().getClass().getSimpleName();
		Annotation annotations[] = calculator.getClass().getAnnotations();
		for (int i = 0; i < annotations.length; i++) {
			Annotation a = annotations[i];
			if (a.annotationType().getName().contains("LabUiAnnotation")) {
				caption = (((LabUiAnnotation)a).caption());
			}
		}
		return caption;
	}
	
	public Class getCalculatorClass() {
		return Calculator.class;
		//return calculator.getClass();
	}

	public void clear() {
		this.calculator.clear();
		for (String key : this.inConnector.keySet()) {
			this.inConnector.get(key).clear();
		}
		for (String key : this.outConnector.keySet()) {
			this.outConnector.get(key).clear();
		}		
	}

	protected boolean isAliveConnectorChanged(String key, String code) {
		boolean ret = true;
		if (aliveConnectors.get(key) != null) {
			ret = this.aliveConnectors.get(key).equals(code);
		}
		
		this.aliveConnectors.put(key, code);
		return !ret;
	}
}
